#include <caemugs/CAEmuGSE.h>

// ***********************************************************************

// class EDROOMTopContext

// ***********************************************************************



	// CONSTRUCTORES***********************************************

CAEmuGS::EDROOMTopContext::EDROOMTopContext (CAEmuGS &act, word32_t & EDROOMpVarVAcks, byte_t & EDROOMpVarVAltBit, bool_t & EDROOMpVarVAlternate, BurstTypeReceived_t & EDROOMpVarVBurstType, CDDataBurst & EDROOMpVarVDataBurst, bool_t & EDROOMpVarVFaseDos, CPackListHandler & EDROOMpVarVFreePackList, int & EDROOMpVarVmessageNumber, CPackListHandler & EDROOMpVarVTCPackList, CEDROOMPOOLCDBurst & EDROOMpPoolCDBurst, CEDROOMPOOLCDDataBurst & EDROOMpPoolCDDataBurst ):

	EDROOMcomponent(act)
	,Msg(EDROOMcomponent.Msg)
	,MsgBack(EDROOMcomponent.MsgBack)
	,SgiTMTCServer(EDROOMcomponent.SgiTMTCServer)
	,SGIGround(EDROOMcomponent.SGIGround)
	, VAcks ( EDROOMpVarVAcks )
	, VAltBit ( EDROOMpVarVAltBit )
	, VAlternate ( EDROOMpVarVAlternate )
	, VBurstType ( EDROOMpVarVBurstType )
	, VDataBurst ( EDROOMpVarVDataBurst )
	, VFaseDos ( EDROOMpVarVFaseDos )
	, VFreePackList ( EDROOMpVarVFreePackList )
	, VmessageNumber ( EDROOMpVarVmessageNumber )
	, VTCPackList ( EDROOMpVarVTCPackList )
	, CEDROOMPoolCDBurst ( EDROOMpPoolCDBurst )
	, CEDROOMPoolCDDataBurst ( EDROOMpPoolCDDataBurst )
 {

}

CAEmuGS::EDROOMTopContext::EDROOMTopContext ( EDROOMTopContext &contex ):

	EDROOMcomponent(contex.EDROOMcomponent),
	Msg(contex.Msg),
	MsgBack(contex.MsgBack)
	,SgiTMTCServer(contex.SgiTMTCServer)
	,SGIGround(contex.SGIGround)
	, VAcks ( contex.VAcks )
	, VAltBit ( contex.VAltBit )
	, VAlternate ( contex.VAlternate )
	, VBurstType ( contex.VBurstType )
	, VDataBurst ( contex.VDataBurst )
	, VFaseDos ( contex.VFaseDos )
	, VFreePackList ( contex.VFreePackList )
	, VmessageNumber ( contex.VmessageNumber )
	, VTCPackList ( contex.VTCPackList )
	, CEDROOMPoolCDBurst ( contex.CEDROOMPoolCDBurst )
	, CEDROOMPoolCDDataBurst ( contex.CEDROOMPoolCDDataBurst )
 {

}

	// EDROOMSearchContextTrans********************************************

bool CAEmuGS::EDROOMTopContext::EDROOMSearchContextTrans(TEDROOMTransId &edroomCurrentTrans){

	bool edroomValidMsg=false; 

	 switch(Msg->signal){

		 case ( EDROOMSignalDestroy ): 

			 edroomValidMsg=true;
			 edroomCurrentTrans.distanceToContext = 0 ;
			 edroomCurrentTrans.localId = -1 ;
			 break;

	}

	return(edroomValidMsg);

}

	// Funciones Definidas por el usuario  ****************************

void	CAEmuGS::EDROOMTopContext::FCheckTMAcks(bool_t *pAcksOk,bool_t *allOk,byte_t num,CPackListHandler &packList)

{
	int32_t j=1;
	CPackHandler newPackHandler;
	byte_t dataBurst[256];

	if(packList.GetPackCounter())
	{
		packList.ToHeadPack();
		do
		{
			packList.GetCurrentReadPack(newPackHandler);
			if (newPackHandler.CheckCRC(0,254))
			{
				pAcksOk[j-1]=true;
				for (int32_t i=0; i<256;i++)
				{
					newPackHandler.ReadByte(i,dataBurst[i]);
				}
				FStoreTMFrame(dataBurst);//TODO:TEMPORAL

			}
			else
			{

				(*allOk)=false;
			}
			j++;
		}
		while (j<=num && packList.NextReadPack());
	}

}



bool_t	CAEmuGS::EDROOMTopContext::GIsSRxDataBurst()

{
#ifdef CONFIG_EDROOM_GLUE_SWR_TMTC_DELIVERY_SERVICE
	return (VBurstType==BurstTypeIsSenTMTCDelAccept) || (VBurstType==BurstTypeIsSenTCSgiLinkFramesAck);
#else
	return (VBurstType==BurstTypeIsSatTCFramesAck);
#endif
}



void	CAEmuGS::EDROOMTopContext::FIntToChar(int32_t num,byte_t * cConvert)

{

	byte_t aux;
	int32_t i=0;
	int32_t j=0;

	do
	{ 
		cConvert[i++]= (num % 10) + '0';
		num=num/10;
	}
	while (num>0);
	cConvert[i--]='\0';
	for (j=0;i>j;i--,j++)
	{
		aux=cConvert[i];
		cConvert[i]=cConvert[j];
		cConvert[j]=aux;
	}
}



void	CAEmuGS::EDROOMTopContext::FListening()

{

}



void	CAEmuGS::EDROOMTopContext::FPrepareNextTC()

{
	FCopyPacks();
#ifdef CONFIG_EDROOM_GLUE_SWR_TMTC_DELIVERY_SERVICE
#ifdef CONFIG_DEMO_SAF_IT_SWR_DEMO
	if (VFaseDos)
	{
		DEBUG("GS: VDataBurst: build_sen_tc_sgi_header_frame \n");
		build_sen_tc_sgi_header_frame(VDataBurst.header,VTCPackList.GetPackCounter(),VAlternate,&VAltBit,VAcks);
	}
	else
	{
		DEBUG("GS: VDataBurst: build_sen_tc_sgi_header_frame \n");
		build_sen_tc_sgi_header_frame(VDataBurst.header,0,VAlternate,&VAltBit,VAcks);
	}
#else
	build_sen_tc_sgi_header_frame(VDataBurst.header,VTCPackList.GetPackCounter(),VAlternate,&VAltBit,VAcks);
#endif
#else
	header_tc_frame(VDataBurst.header,VTCPackList.GetPackCounter());
#endif

}



void	CAEmuGS::EDROOMTopContext::FReceiveAndRespondBurst()

{
	int8_t offSet;
	VBurstType=BurstTypeIsSGIDefault;

	// reply
	CDBurst & varCDBurst = *(CDBurst *)Msg->data;
	if (sgi_link_layer_check_crcs(varCDBurst.header,&offSet))
	{
		VBurstType=get_burst_type(varCDBurst.header+offSet);
	}

}




void	CAEmuGS::EDROOMTopContext::FReceiveAndRespondDataBurst()

{
	int8_t offSet;
	VBurstType=BurstTypeIsSGIDefault;

	// reply
	CDDataBurst & varCDDataBurst = *(CDDataBurst *)Msg->data;
	if (sgi_link_layer_check_crcs(varCDDataBurst.header,&offSet))
	{
		VBurstType=get_burst_type(varCDDataBurst.header+offSet);
	}

}



void	CAEmuGS::EDROOMTopContext::FReceiveTCLists()

{
	CDTMTCCommServer & varCDTMTCCommServer = *(CDTMTCCommServer *)Msg->data;

	VTCPackList=varCDTMTCCommServer.packList1;
	VFreePackList=varCDTMTCCommServer.packList2;

}



void	CAEmuGS::EDROOMTopContext::FSendDSRxBurst()

{
	CDBurst * ap_CDBurst = CEDROOMPoolCDBurst.AllocData();
#ifdef CONFIG_EDROOM_GLUE_SWR_TMTC_DELIVERY_SERVICE
	// Complete Data

#ifdef CONFIG_CAEMUPLANNER_SWR_TEST_MULTI_TM
#ifdef CONFIG_DEMO_SAF_IT_SWR_DEMO
	word32_t sizeReq=96*256;
#else
	word32_t sizeReq=95*256;
#endif
#else
	word32_t sizeReq=12*256;
#endif
	if (!VFaseDos)
	{
		DEBUG("GS: req 96 ap_CDBurst: build_sen_tm_tc_del_req \n");
		build_sen_tm_tc_del_req (ap_CDBurst->header,sizeReq,0);
	}
	else
	{
		DEBUG("GS: req 0 ap_CDBurst: build_sen_tm_tc_del_req \n");
		build_sen_tm_tc_del_req (ap_CDBurst->header,0,VTCPackList.GetPackCounter());
	}
#else
	int8_t offSet;
	bool_t acksOk[32];
	bool_t allOk=true;
	for (int32_t i=0;i<32;i++)
	{
		acksOk[i]=false;
	}

	CDDataBurst & varCDDataBurst = *(CDDataBurst *)Msg->data;
	sgi_link_layer_check_crcs(varCDDataBurst.header,&offSet);

	FCheckTMAcks(acksOk,&allOk,sgi_link_layer_get_nf(varCDDataBurst.header+offSet),varCDDataBurst.packList);

	VAcks=set_acks(acksOk);//TODO:tambien hay funcion en PackMemory
	// Complete Data
	if (sgi_link_layer_is_last_sat_tm_frames(varCDDataBurst.header+offSet) && allOk)
	{
		build_tm_ack(ap_CDBurst->header,VAcks,true);
	}
	else
	{
		build_tm_ack(ap_CDBurst->header,VAcks,false);
	}

#endif
	// reply
	Msg->reply(SRxBurst, ap_CDBurst, &CEDROOMPoolCDBurst);
}



void	CAEmuGS::EDROOMTopContext::FSendDSRxDataBurst()

{
#ifdef CONFIG_EDROOM_GLUE_SWR_TMTC_DELIVERY_SERVICE
	int8_t offSet = 0;
	bool_t acksOk[32];
	bool_t allOk = true;
	byte_t packsNumber = 32;
	word32_t acksAux = 0;
	CDDataBurst & varCDDataBurst = *(CDDataBurst *) Msg->data;
	int32_t i = 0;

	for (i = 0; i<32; i++)
	{
		acksOk[i] = false;
	}

	sgi_link_layer_check_crcs(varCDDataBurst.header, &offSet);
	FCheckTMAcks(acksOk, &allOk, sgi_link_layer_get_nf(varCDDataBurst.header+offSet), varCDDataBurst.packList);
	VAcks = set_acks(acksOk);


#ifdef CONFIG_DEMO_SAF_IT_SWR_DEMO
	if ((VFaseDos==true) && VTCPackList.GetPackCounter())//quedan TCs
#else
		if (VTCPackList.GetPackCounter())//quedan TCs
#endif
		{
			//obtenemos los acks de la anterior rafaga
			acksAux=sgi_link_layer_get_acks(varCDDataBurst.header+offSet);


			VDataBurst.packList.ExtractAllPackages(VFreePackList);//Recuperar memoria de los paquetes
			VTCPackList.ExtractPackages(acksAux, packsNumber, VFreePackList);
			CDDataBurst * ap_CDDataBurst = CEDROOMPoolCDDataBurst.AllocData();
			FPrepareNextTC();
			*ap_CDDataBurst = VDataBurst;
			Msg->reply(SRxDataBurst, ap_CDDataBurst, &CEDROOMPoolCDDataBurst);
		}
		else
		{

			CDBurst * ap_CDBurst = CEDROOMPoolCDBurst.AllocData();
			if (sgi_link_layer_is_last_sen_tm_sgi_link_frames(varCDDataBurst.header+offSet)&&allOk)
			{
				build_sen_tm_sgi_frames_ack(ap_CDBurst->header, VAcks, true);
			}
			else
			{
				build_sen_tm_sgi_frames_ack(ap_CDBurst->header, VAcks, false);
			}
			Msg->reply(SRxBurst, ap_CDBurst, &CEDROOMPoolCDBurst);
		}
#else
	CDDataBurst * ap_CDDataBurst = CEDROOMPoolCDDataBurst.AllocData();
	/*
	 \*
	 \* TODO: SACAR LOS DATOS SI LA TRAMA READY LLEVARA
	 \*
	 */
	FPrepareNextTC();
	*ap_CDDataBurst=VDataBurst;
	Msg->reply(SRxDataBurst, ap_CDDataBurst, &CEDROOMPoolCDDataBurst);
#endif
}





void	CAEmuGS::EDROOMTopContext::FSendNoAnswer()

{
	Msg->reply(SNoAnswer);
}



void	CAEmuGS::EDROOMTopContext::FSendSRxDataBurst()

{

	byte_t packsNumber=32;
	int8_t offSet;
	byte_t acks[4];
	word32_t acksAux;
	CDDataBurst * ap_CDDataBurst = CEDROOMPoolCDDataBurst.AllocData();

	if (VBurstType==BurstTypeIsSenTCSgiLinkFramesAck || VBurstType==BurstTypeIsSatTCFramesAck)//si no no recoge nada
	{
		CDBurst & varCDBurst = *(CDBurst *)Msg->data;
		sgi_link_layer_check_crcs(varCDBurst.header,&offSet);

		acksAux=sgi_link_layer_get_acks(varCDBurst.header+offSet);

		VDataBurst.packList.ExtractAllPackages(VFreePackList);//Recuperar memoria de los paquetes
		VTCPackList.ExtractPackages(acksAux,packsNumber,VFreePackList);
		VAcks=0xFFFFFFFF;
	}
	/*
	\*
	\* TODO: En este caso habra que mirar si el ack es repetido,
	\* u otros campos........utilizar contador y si es 5 ready
	 */
	FPrepareNextTC();
	*ap_CDDataBurst=VDataBurst;

	Msg->reply(SRxDataBurst, ap_CDDataBurst, &CEDROOMPoolCDDataBurst);
}



void	CAEmuGS::EDROOMTopContext::FStoreTMFrame(byte_t * pFrames)

{
	char ruta[50];
	byte_t aux[10];
	FILE *fichero;
#ifdef CONFIG_CAEMUPLANNER_SWR_TEST_MULTI_TM
	strcpy(ruta,"EndMultiTM/");
#endif
#ifdef CONFIG_CAEMUPLANNER_SWR_TEST_ONE_TM
	strcpy(ruta,"EndOneTM/");
#endif

	FIntToChar(pFrames[0],aux);
	strcat(ruta,(char *)aux);
	fichero=fopen(ruta,"ab");
	if (fichero!=NULL)
	{
		//es entre 2 por que el valor va en bytes pero escribe en words
		fwrite(pFrames+2,1,(pFrames[2]+2), fichero);
		fclose(fichero);
	}


}



bool_t	CAEmuGS::EDROOMTopContext::GIsDSRxBurst()

{
#ifdef CONFIG_EDROOM_GLUE_SWR_TMTC_DELIVERY_SERVICE
	return (VBurstType==BurstTypeIsSrgHumsatReady);
#else
	return (VBurstType==BurstTypeIsSatTMFrames);
#endif
}




bool_t	CAEmuGS::EDROOMTopContext::GIsDSRxDataBurst()

{
#ifdef CONFIG_EDROOM_GLUE_SWR_TMTC_DELIVERY_SERVICE
	return (VBurstType==BurstTypeIsSenTMSgiLinkFrames);
#else
	return (VBurstType==BurstTypeIsSrgHumsatReady);
#endif
}



void	CAEmuGS::EDROOMTopContext::FCopyPacks()

{
	byte_t newPack[256];
	word16_t packsNumber=32;
	int8_t num=0;
	int8_t randNum;
	CPackHandler newReadPackHandler;
	CPackHandler newEditPackHandler;

	srand (time(NULL));//TODO: para el test

	if ( VTCPackList.GetPackCounter()&&(VFreePackList.GetPackCounter()>=32) )
	{
		VFreePackList.ExtractPackages(packsNumber, VDataBurst.packList);
		VTCPackList.ToHeadPack();
		VDataBurst.packList.ToHeadPack();
		do
		{
			randNum = (rand()%32);//TODO: para provocar el error

			VDataBurst.packList.GetCurrentEditPack(newEditPackHandler);
			VTCPackList.GetCurrentReadPack(newReadPackHandler);
			for (int32_t i = 0; i<256; i++)
			{
				newReadPackHandler.ReadByte(i, newPack[i]);
				if ( num!=randNum )//TODO: para provocar el error
				{
					newEditPackHandler.WriteByte(i, newPack[i]);
				}
				else
				{
					newEditPackHandler.WriteByte(i, 0);
				}
			}
			VDataBurst.packList.NextEditPack();
			num++;
		}
		while (VTCPackList.NextReadPack()&&num<32);
	}


}




bool_t	CAEmuGS::EDROOMTopContext::GIsSRxBurst()

{
	DEBUG("");
	return (VBurstType==BurstTypeIsSenTMTCDelEnd);
}



void	CAEmuGS::EDROOMTopContext::FSendSRxBurst()

{
	DEBUG("FASE DOS\n");
	VFaseDos=true;

	CDDataBurst & varCDDataBurst = *(CDDataBurst *)Msg->data;
	CDBurst * ap_CDBurst = CEDROOMPoolCDBurst.AllocData();

	build_sen_tm_tc_del_end_ack(ap_CDBurst->header);


	// reply
	Msg->reply(SRxBurst, ap_CDBurst, &CEDROOMPoolCDBurst);


}



	//*****Pools

	//CEDROOMPOOLCDBurst

CAEmuGS::EDROOMTopContext::CEDROOMPOOLCDBurst::CEDROOMPOOLCDBurst(unsigned int elemCount, CDBurst* pMem, TEDROOMBool * pMemMarks):CEDROOMProtectedMemoryPool(elemCount, pMem, pMemMarks, sizeof (CDBurst)){;}

CDBurst *	CAEmuGS::EDROOMTopContext::CEDROOMPOOLCDBurst::AllocData(){
	return(CDBurst*)CEDROOMProtectedMemoryPool::AllocData();
}

	//CEDROOMPOOLCDDataBurst

CAEmuGS::EDROOMTopContext::CEDROOMPOOLCDDataBurst::CEDROOMPOOLCDDataBurst(unsigned int elemCount, CDDataBurst* pMem, TEDROOMBool * pMemMarks):CEDROOMProtectedMemoryPool(elemCount, pMem, pMemMarks, sizeof (CDDataBurst)){;}

CDDataBurst *	CAEmuGS::EDROOMTopContext::CEDROOMPOOLCDDataBurst::AllocData(){
	return(CDDataBurst*)CEDROOMProtectedMemoryPool::AllocData();
}



// ***********************************************************************

// class EDROOMTopState

// ***********************************************************************



	// CONSTRUCTOR*************************************************

CAEmuGS::EDROOMTopState::EDROOMTopState (CAEmuGS &act, CEDROOMMemory *pEDROOMMemory  ):
	  EDROOMTopContext( act , VAcks, VAltBit, VAlternate, VBurstType, VDataBurst, VFaseDos, VFreePackList, VmessageNumber, VTCPackList,CEDROOMPoolCDBurst,CEDROOMPoolCDDataBurst )
	, VAcks ( 0x00000000 )
	, VAltBit ( 0x00 )
	, VAlternate ( true )
	, VBurstType ( BurstTypeIsSGIDefault )
	, VDataBurst (   )
	, VFaseDos ( false )
	, VFreePackList (   )
	, VmessageNumber ( 1 )
	, VTCPackList (   )
	, CEDROOMPoolCDBurst ( 3, pEDROOMMemory->poolCDBurst, pEDROOMMemory->poolMarkCDBurst)
	, CEDROOMPoolCDDataBurst ( 3, pEDROOMMemory->poolCDDataBurst, pEDROOMMemory->poolMarkCDDataBurst)
{

}

	// EDROOMBehaviour**********************************************

void CAEmuGS::EDROOMTopState::EDROOMBehaviour(){

	TEDROOMTransId edroomCurrentTrans;

	edroomCurrentTrans = EDROOMIllegada();

	do{

		switch(edroomCurrentTrans.localId){

			case(Init):
				 
				edroomNextState = Ready;
				break;
			case(ListeningDataBurst):
				FReceiveAndRespondDataBurst();

				EDROOMcomponent.EDROOMTraceStateEntry();

				if( GIsDSRxDataBurst() ){
					FSendDSRxDataBurst();
					edroomCurrentTrans.localId = ListeningDataBurst_DSRxDataBurst;
					edroomNextState = Ready;
				 } 
				else if( GIsDSRxBurst() ){
					FSendDSRxBurst();
					edroomCurrentTrans.localId = ListeningDataBurst_DSRxBurst;
					edroomNextState = Ready;
				 } 
				else{
					FSendNoAnswer();
					edroomCurrentTrans.localId = ListeningDataBurst_DSNoAnswer;
					edroomNextState = Ready;
				 } 
				EDROOMcomponent.EDROOMTraceTrans(edroomCurrentTrans, Msg->signal);
				break;
			case(ListeningBurst):
				FReceiveAndRespondBurst();

				EDROOMcomponent.EDROOMTraceStateEntry();

				if( GIsSRxDataBurst() ){
					FSendSRxDataBurst();
					edroomCurrentTrans.localId = ListeningBurst_SRxDataBurst;
					edroomNextState = Ready;
				 } 
				else if( GIsSRxBurst() ){
					FSendSRxBurst();
					edroomCurrentTrans.localId = ListeningBurst_SRxBurst;
					edroomNextState = Ready;
				 } 
				else{
					edroomCurrentTrans.localId = ListeningBurst_SNoAnswer;
					edroomNextState = Ready;
				 } 
				EDROOMcomponent.EDROOMTraceTrans(edroomCurrentTrans, Msg->signal);
				break;
			case(ReceiveTCLists):
				FReceiveTCLists();
				edroomNextState = Ready;
				break;
		}

		EDROOMcomponent.EDROOMTraceStateEntry();

		EDROOMcomponent.EDROOMTraceStateEntry();

		switch(edroomNextState){

			case(I):
				edroomCurrentTrans=EDROOMIllegada();
				break;

			case(Ready):
				
				edroomCurrentTrans=EDROOMReadyllegada();
				break;

		}

		EDROOMcomponent.EDROOMTraceStateExit();

		edroomCurrentState=edroomNextState;

	}while(Msg->signal != EDROOMSignalDestroy);

}



	// Inicio**********************************************

void CAEmuGS::EDROOMTopState::EDROOMInicio(){

edroomCurrentState=I;

}



//	 ***********************************************************************

//	 llegada SubEstado I

//	 ***********************************************************************



TEDROOMTransId CAEmuGS::EDROOMTopState::EDROOMIllegada(){

	TEDROOMTransId edroomCurrentTrans;

	edroomCurrentTrans.localId = Init;
	edroomCurrentTrans.distanceToContext = 0;
	EDROOMcomponent.EDROOMTraceTrans(edroomCurrentTrans, 0);

	return(edroomCurrentTrans);

}



	// ***********************************************************************

	// llegada SubEstado Hoja Ready

	// ***********************************************************************



TEDROOMTransId CAEmuGS::EDROOMTopState::EDROOMReadyllegada(){

	TEDROOMTransId edroomCurrentTrans;

	int edroomValidMsg=false;

	do{

		EDROOMNuevoMens ();

		switch(Msg->signal){

			case (STxDataBurst): 

				 if ( *Msg->GetPInterface() == SGIGround) {

					edroomCurrentTrans.localId = ListeningDataBurst;
					edroomCurrentTrans.distanceToContext = 0 ;
					edroomValidMsg=true;
				 }

				break;

			case (STxBurst): 

				 if ( *Msg->GetPInterface() == SGIGround) {

					edroomCurrentTrans.localId = ListeningBurst;
					edroomCurrentTrans.distanceToContext = 0 ;
					edroomValidMsg=true;
				 }

				break;

			case (EmuGSDoublePackList): 

				 if ( *Msg->GetPInterface() == SgiTMTCServer) {

					edroomCurrentTrans.localId= ReceiveTCLists;
					edroomCurrentTrans.distanceToContext = 0;
					edroomValidMsg=true;
				 }

				break;

		};

		if (!edroomValidMsg) edroomValidMsg = EDROOMSearchContextTrans(edroomCurrentTrans);

	} while (!edroomValidMsg);

	EDROOMcomponent.EDROOMTraceTrans(edroomCurrentTrans, Msg->signal);

	return(edroomCurrentTrans);

}



